﻿/*HandBrake Batch Encoder Mk II 
 * v2.010 5/16/12 
 * Steven Hawkes
 *
 * This class is a business class whoch provides all of the key operations that need to be 
 * performed on Presets.   There are two types of Presets that are managed by this class, 
 * Default Presets and Custom presets.    
 * 
 * Default presets are those that are provided by default when Handbrake is installed on your PC.    
 * These presets are contained in a presets.xml file in the users roaming profile.    In this application
 * I have decided that these presets are Read Only for the user.   The user can use thembut not changeethod 
 * them.  The user can off course use a default preset and modify it and use it however if the user then 
 * wants to save it for later use, then they need to create a custom Preset
 * 
 * Custom presets are user defined presets created generally as a clone from one of the default presets.
 * Operations are provided to load and save these presets also in the users roaming profile.  The reason to 
 * keep these seperate is two fold.   Firstly, installing a new version of Handbrake should not loose the 
 * users custom presets and secondly, the XML structure used in Handbrake to represent a Preset is overkill
 * for this batch application.
 * 
 * This class provides an Import method to load both the Default and Custom presets into the class.   It them provides
 * methods by which the user can access these presets either based on Preset type or as a combined list.
 * 
 * The class also provides basic functions by which the user can perform the following 
 * 
 *       Get a Preset by name
 * 
 * It provides the following basic operations which should be self explanitory.
 * 
 *       public void ExportPresets()
 *       public void ImportPresets()
 *       public PresetType GetPresetType(string name)
 *       public int GetIndexInMergedPresetList(string name)
 *       public List<Preset> GetDefaultPresets()
 *       public List<Preset> GetCustomPresets()
 *       public bool AddPreset(PresetType presetType, Preset preset)
 *       public Preset FindPresetByName(string name)
 *       public Preset FindPresetByName(PresetType presetType, string name)
 *       public bool RemovePresetByName(PresetType presetType, string name)
 *
 * The methods have been created to address the needs of this application and will over time be
 * optimized further.
 * 
 * I intend to add the Log4Net logging facility to thsi class in the future to handle the logging of errors
 * but this will be in a future release
 * 
 */

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Linq;
using log4net;

namespace HandBrake_Encoder
{
    public class Presets
    {
        #region PresetType enum

        public enum PresetType
        {
            Custom = 0,
            Default,
            Unknown
        };

        #endregion

        private static List<Preset> CustomPresets = new List<Preset>();
        private static readonly List<Preset> DefaultPresets = new List<Preset>();
        public static ILog Log = LogManager.GetLogger("Presets");

        public Presets()
        {
            // Only load the defaults the first time this class is called.
            // There shall always be a set of defaults else Handbrake has not been installed or there is an error.
            if (DefaultPresets.Count == 0)
            {
                ImportPresets();
            }
        }

        private bool ImportPresets()
        {
            bool status = false;
            try
            {
                string handbrakeLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                Log.Debug("Default Presets are being imported from location : " + handbrakeLocation +"\\HandBrake\\presets.xml");
              
                IEnumerable<Preset> presets =
                    from c in XElement.Load(handbrakeLocation + "\\HandBrake\\presets.xml").Elements("Preset")
                    let xElement = c.Element("Name")
                    where xElement != null
                    let element = c.Element("Query")
                    where element != null
                    select new Preset
                               {
                                   Name = xElement.Value,
                                   Query = element.Value
                               };

           
                DefaultPresets.AddRange(presets);
                Log.Debug(DefaultPresets.Count + " Default Presets Loaded");

                Log.Debug("Custom Presets are being imported from location : " + handbrakeLocation + "\\HandBrake\\customPresets.bin");
          
                // Import the Custom Presets
                using (Stream stream = File.Open(handbrakeLocation + "\\HandBrake\\customPresets.bin", FileMode.Open))
                {
                    var bin = new BinaryFormatter();
                    CustomPresets = (List<Preset>) bin.Deserialize(stream);
                    Log.Debug(CustomPresets.Count.ToString(CultureInfo.InvariantCulture) + " Custom Presets Loaded");
                }

                status = true;
            }
            catch (Exception ex)
            {
                Log.Error("Exception raised when loading presets : " + ex.Message);
                status = false;
            }
            return status;
        }

        public void ExportPresets()
        {
            Log.Debug("Exporting Custom Presets");
            try
            {
                string handbrakeLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                using (Stream stream = File.Open(handbrakeLocation + "\\HandBrake\\customPresets.bin", FileMode.Create))
                {
                    var bin = new BinaryFormatter();
                    bin.Serialize(stream, CustomPresets);
                    Log.Debug("Custom Presets Exported Successfully");
                }
            }
            catch (IOException)
            {
                Log.Error("ErrorSaving Custom Presets");
            }
        }

        public PresetType GetPresetType(string name)
        {
            Preset result = DefaultPresets.Find(
                pr => pr.Name == name
                );

            if (result != null)
            {
                return PresetType.Default;
            }

            result = CustomPresets.Find(
                pr => pr.Name == name
                );

            if (result != null)
            {
                Log.Debug("Preset Located");
                return PresetType.Custom;
            }

            Log.Debug("Preset not found");
            return PresetType.Unknown;
        }


        public int GetIndexInMergedPresetList(string name)
        {
            List<Preset> mergedList = GetMergedPresetList();
            Preset tempPreset = FindPresetByName(name);
            return mergedList.IndexOf(tempPreset);
        }

        public List<Preset> GetMergedPresetList()
        {
            var mergedList = new List<Preset>();

            if (DefaultPresets.Count > 0)
            {
                mergedList.AddRange(DefaultPresets);
            }

            if (CustomPresets.Count > 0)
            {
                mergedList.AddRange(CustomPresets);
            }

            return mergedList;
        }

        public List<Preset> GetDefaultPresets()
        {
            return DefaultPresets;
        }

        public List<Preset> GetCustomPresets()
        {
            return CustomPresets;
        }

        public void AddPreset(PresetType presetType, Preset preset)
        {
            // Note only a custom preset can be added 
            switch (presetType)
            {
                case PresetType.Custom:
                    if (FindPresetByName(PresetType.Custom, preset.Name) != null)
                    {
                        RemovePresetByName(PresetType.Custom, preset.Name);
                    }
                    CustomPresets.Add(preset);
                    break;
            }
        }

        public Preset FindPresetByName(string name)
        {
            Preset result = DefaultPresets.Find(
                pr => pr.Name == name
                );

            if (result != null)
            {
                return result;
            }

            result = CustomPresets.Find(
                pr => pr.Name == name
                );

            if (result != null)
            {
                return result;
            }

            return null;
        }

        public Preset FindPresetByName(PresetType presetType, string name)
        {
            Preset result = null;
            switch (presetType)
            {
                case PresetType.Custom:
                    result = CustomPresets.Find(
                        pr => pr.Name == name
                        );

                    if (result != null)
                    {
                        return result;
                    }
                    break;

                case PresetType.Default:
                    result = DefaultPresets.Find(
                        pr => pr.Name == name
                        );

                    if (result != null)
                    {
                        return result;
                    }
                    break;
            }

            return null;
        }

        public void RemovePresetByName(PresetType presetType, string name)
        {
            // Only a custom preset can be deleted

            switch (presetType)
            {
                case PresetType.Custom:
                    {
                        foreach (Preset preset in CustomPresets)
                        {
                            if (preset.Name.Equals(name))
                            {
                                CustomPresets.Remove(preset);
                                break;
                            }
                        }
                        break;
                    }
            }

        }
    }
}